using System;
using System.Collections.Generic;
using UnityEngine;

namespace ai_library
{
	public class SteeringBehavior
	{
		public enum SummingMethod
		{
			weighted_average,
			prioritized,
			dithered,
		};

		public IEntity PursuitTarget
		{
			get;
			set;
		}

		public IEntity EvadeTarget
		{
			get;
			set;
		}

		public IEntity HideTarget
		{
			get;
			set;
		}

		public Vector2 SeekTarget
		{
			get;
			set;
		}

		public Vector2 FleeTarget
		{
			get;
			set;
		}

		public Vector2 ArriveTarget
		{
			get;
			set;
		}

		public SteeringBehavior (IEntity entity)
		{
			m_entity = entity;

			m_weightCohesion = 1.0f;
			m_weightAlignment = 1.0f;
			m_weightSeparation = 1.0f;
			m_weightSeek = 1.0f;
			m_weightFlee = 1.0f;
			m_weightArrive = 1.0f;
			m_weightOffsetPursuit = 1.0f;
			//m_weightInterpose = 1.0f;
			//m_weightHide = 1.0f;

			m_weightObstacleAvoidance = 0.8f;
			m_weightWallAvoidance = 1.0f;

			m_weightWander = 0.05f;
			m_weightPursuit = 0.2f;
			m_weightEvade = 0.4f;

			m_weightFollowPath = 1.0f;

			m_behaviorFlags = 0;

			//m_path = new List<Vector2>();

			//m_deceleration = Deceleration.normal;

			m_cellSpaceOn = false;

			m_summingMethod = SummingMethod.prioritized;

			PursuitTarget = null;
			EvadeTarget = null;
			HideTarget = null;

			SeekTarget = Vector2.zero;
			FleeTarget = Vector2.zero;
			ArriveTarget = Vector2.zero;

			//m_viewDistance = 5.0f;

			m_wanderDistance = 5.0f;
			m_wanderJitter = 0.01f;
			m_wanderRadius = 3.0f;

			//m_waypointSeekDistSq = 10.0f;

			m_wanderTarget = UnityEngine.Random.insideUnitCircle * m_wanderRadius;
		}

		//calculates and sums the steering forces from any active behaviors
		public Vector2 Calculate()
		{
			//reset the steering force
			m_steeringForce = Vector2.zero;
			
			//use space partitioning to calculate the neighbours of this vehicle
			//if switched on. If not, use the standard tagging system
//			if (!IsSpacePartitioningOn())
//			{
//				//tag neighbors if any of the following 3 group behaviors are switched on
//				if (On(BehaviorType.separation) || On(BehaviorType.allignment) || On(BehaviorType.cohesion))
//				{
//				}
//			}
//			else
//			{
//				//calculate neighbours in cell-space if any of the following 3 group
//				//behaviors are switched on
//				if (On(BehaviorType.separation) || On(BehaviorType.allignment) || On(BehaviorType.cohesion))
//				{
//				}
//			}
			
			switch (m_summingMethod)
			{
			case SummingMethod.weighted_average:
				m_steeringForce = CalculateWeightedSum();
				break;
				
			case SummingMethod.prioritized:
				m_steeringForce = CalculatePrioritized();
				break;
				
			case SummingMethod.dithered:
				m_steeringForce = CalculateDithered();
				break;
			}
			
			return m_steeringForce;
		}
		
		//calculates the component of the steering force that is parallel
		//with the vehicle heading
		public float ForwardComponent()
		{
			return Vector2.Dot(m_entity.Forward, m_steeringForce);
		}
		
		//calculates the component of the steering force that is perpendicuar
		//with the vehicle heading
		public float SideComponent()
		{
			return Vector2.Dot(m_entity.Right, m_steeringForce);
		}

		public void SetOffset(Vector2 offset)
		{
			m_offset = offset;
		}

		public Vector2 GetOffset()
		{
			return m_offset;
		}
		
//		public void SetPath(List<Vector2> path)
//		{
//			m_path = path;
//		}

		public void CreateRandomPath(int numWaypoints, int mx, int my, int cx, int cy)
		{
			//todo
			//m_path->CreateRandomPath(numWaypoints, mx, my, cx, cy);
		}
		
		public Vector2 Force()
		{
			return m_steeringForce;
		}
		
		public void ToggleSpacePartitioningOnOff()
		{
			m_cellSpaceOn = !m_cellSpaceOn;
		}

		public bool IsSpacePartitioningOn()
		{
			return m_cellSpaceOn;
		}
		
		public void SetSummingMethod(SummingMethod sm)
		{
			m_summingMethod = sm;
		}

		// Flags
		public void FleeOn(){m_behaviorFlags |= BehaviorType.flee;}
		public void SeekOn(){m_behaviorFlags |= BehaviorType.seek;}
		public void ArriveOn(){m_behaviorFlags |= BehaviorType.arrive;}
		public void WanderOn(){m_behaviorFlags |= BehaviorType.wander;}
		public void PursuitOn(IEntity a){m_behaviorFlags |= BehaviorType.pursuit; PursuitTarget = a;}
		public void EvadeOn(IEntity a){m_behaviorFlags |= BehaviorType.evade; EvadeTarget = a;}
		public void CohesionOn(){m_behaviorFlags |= BehaviorType.cohesion;}
		public void SeparationOn(){m_behaviorFlags |= BehaviorType.separation;}
		public void AlignmentOn(){m_behaviorFlags |= BehaviorType.allignment;}
		public void ObstacleAvoidanceOn(){m_behaviorFlags |= BehaviorType.obstacle_avoidance;}
		public void WallAvoidanceOn(){m_behaviorFlags |= BehaviorType.wall_avoidance;}
		public void FollowPathOn(){m_behaviorFlags |= BehaviorType.follow_path;}
		public void InterposeOn(Agent a1, Agent a2){m_behaviorFlags |= BehaviorType.interpose;}
		public void HideOn(Agent a){m_behaviorFlags |= BehaviorType.hide;}
		public void OffsetPursuitOn(Agent a1, Vector2 offset){m_behaviorFlags |= BehaviorType.offset_pursuit;}  
		public void FlockingOn(){CohesionOn(); AlignmentOn(); SeparationOn(); WanderOn();}
		
		public void FleeOff()  {if(On(BehaviorType.flee))   m_behaviorFlags ^=BehaviorType.flee;}
		public void SeekOff()  {if(On(BehaviorType.seek))   m_behaviorFlags ^=BehaviorType.seek;}
		public void ArriveOff(){if(On(BehaviorType.arrive)) m_behaviorFlags ^=BehaviorType.arrive;}
		public void WanderOff(){if(On(BehaviorType.wander)) m_behaviorFlags ^=BehaviorType.wander;}
		public void PursuitOff(){if(On(BehaviorType.pursuit)) m_behaviorFlags ^=BehaviorType.pursuit; PursuitTarget = null; }
		public void EvadeOff(){if(On(BehaviorType.evade)) m_behaviorFlags ^=BehaviorType.evade; EvadeTarget = null; }
		public void CohesionOff(){if(On(BehaviorType.cohesion)) m_behaviorFlags ^=BehaviorType.cohesion;}
		public void SeparationOff(){if(On(BehaviorType.separation)) m_behaviorFlags ^=BehaviorType.separation;}
		public void AlignmentOff(){if(On(BehaviorType.allignment)) m_behaviorFlags ^=BehaviorType.allignment;}
		public void ObstacleAvoidanceOff(){if(On(BehaviorType.obstacle_avoidance)) m_behaviorFlags ^=BehaviorType.obstacle_avoidance;}
		public void WallAvoidanceOff(){if(On(BehaviorType.wall_avoidance)) m_behaviorFlags ^=BehaviorType.wall_avoidance;}
		public void FollowPathOff(){if(On(BehaviorType.follow_path)) m_behaviorFlags ^=BehaviorType.follow_path;}
		public void InterposeOff(){if(On(BehaviorType.interpose)) m_behaviorFlags ^=BehaviorType.interpose;}
		public void HideOff(){if(On(BehaviorType.hide)) m_behaviorFlags ^=BehaviorType.hide;}
		public void OffsetPursuitOff(){if(On(BehaviorType.offset_pursuit)) m_behaviorFlags ^=BehaviorType.offset_pursuit;}
		public void FlockingOff(){CohesionOff(); AlignmentOff(); SeparationOff(); WanderOff();}
		
		public bool isFleeOn(){return On(BehaviorType.flee);}
		public bool isSeekOn(){return On(BehaviorType.seek);}
		public bool isArriveOn(){return On(BehaviorType.arrive);}
		public bool isWanderOn(){return On(BehaviorType.wander);}
		public bool isPursuitOn(){return On(BehaviorType.pursuit);}
		public bool isEvadeOn(){return On(BehaviorType.evade);}
		public bool isCohesionOn(){return On(BehaviorType.cohesion);}
		public bool isSeparationOn(){return On(BehaviorType.separation);}
		public bool isAlignmentOn(){return On(BehaviorType.allignment);}
		public bool isObstacleAvoidanceOn(){return On(BehaviorType.obstacle_avoidance);}
		public bool isWallAvoidanceOn(){return On(BehaviorType.wall_avoidance);}
		public bool isFollowPathOn(){return On(BehaviorType.follow_path);}
		public bool isInterposeOn(){return On(BehaviorType.interpose);}
		public bool isHideOn(){return On(BehaviorType.hide);}
		public bool isOffsetPursuitOn(){return On(BehaviorType.offset_pursuit);}
		
		public float GetWanderJitter(){return m_wanderJitter;}
		public float GetWanderDistance(){return m_wanderDistance;}
		public float GetWanderRadius(){return m_wanderRadius;}
		
		public float GetSeparationWeight(){return m_weightSeparation;}
		public float GetAlignmentWeight(){return m_weightAlignment;}
		public float GetCohesionWeight(){return m_weightCohesion;}

		//--- end public

		private const float wanderRadius = 1.2f;
		private const float wanderDistance = 2.0f;
		private const float wanderJitterPerSec = 80.0f;
		private const float waypointSeekDistance = 20.0f;

		[Flags]
		private enum BehaviorType
		{
			none               = 0x00000,
			seek               = 0x00002,
			flee               = 0x00004,
			arrive             = 0x00008,
			wander             = 0x00010,
			cohesion           = 0x00020,
			separation         = 0x00040,
			allignment         = 0x00080,
			obstacle_avoidance = 0x00100,
			wall_avoidance     = 0x00200,
			follow_path        = 0x00400,
			pursuit            = 0x00800,
			evade              = 0x01000,
			interpose          = 0x02000,
			hide               = 0x04000,
			flock              = 0x08000,
			offset_pursuit     = 0x10000,
		};

		// Uses bitshifting with BehaviorTypes
		private BehaviorType m_behaviorFlags;

		private IEntity m_entity;

		private Vector2 m_steeringForce;
		
		// Used for wandering
		private Vector2 m_wanderTarget;
		private float m_wanderJitter;
		private float m_wanderRadius;
		private float m_wanderDistance;

		// Adjust these to effect the strength of a behavior
		private float m_weightSeparation;
		private float m_weightCohesion;
		private float m_weightAlignment;
		private float m_weightWander;
		private float m_weightObstacleAvoidance;
		private float m_weightWallAvoidance;
		private float m_weightSeek;
		private float m_weightFlee;
		private float m_weightArrive;
		private float m_weightPursuit;
		private float m_weightOffsetPursuit;
		//private float m_weightInterpose;
		//private float m_weightHide;
		private float m_weightEvade;
		private float m_weightFollowPath;

		//private float m_viewDistance;

		//private List<Vector2> m_path ;//-- todo: maybe try using unity navmesh??

		//private float m_waypointSeekDistSq;

		// Offset used for formations or offset pursuit
		private Vector2 m_offset;

		private enum Deceleration
		{
			fast 	= 1,
			normal 	= 2,
			slow 	= 3,
		};

		//private Deceleration m_deceleration;

		// Is Cell Space Partitioning on?
		private bool m_cellSpaceOn;

		private SummingMethod m_summingMethod;

		private bool On(BehaviorType bt)
		{
			return (m_behaviorFlags & bt) == bt;
		}

		private bool AccumulateForce(ref Vector2 sumforce, Vector2 forceToAdd)
		{
			//calculate how much steering force the vehicle has used so far
			float magnitudeSoFar = sumforce.magnitude;
			
			//calculate how much steering force remains to be used by this vehicle
			//todo: make maxforce
			float magnitudeRemaining = m_entity.MaxForce - magnitudeSoFar;
			
			//return false if there is no more force left to use
			if (magnitudeRemaining <= 0.0) 
				return false;
			
			//calculate the magnitude of the force we want to add
			float magnitudeToAdd = forceToAdd.magnitude;
			
			//if the magnitude of the sum of ForceToAdd and the running total
			//does not exceed the maximum force available to this vehicle, just
			//add together. Otherwise add as much of the ForceToAdd vector is
			//possible without going over the max.
			if (magnitudeToAdd < magnitudeRemaining)
			{
				sumforce += forceToAdd;
			}
			else
			{
				//add it to the steering force
				sumforce += forceToAdd.normalized * magnitudeRemaining; 
			}
			
			return true;
		}

		/* .......................................................

                    BEGIN BEHAVIOR DECLARATIONS

      .......................................................*/
		
		
		//this behavior moves the Agent towards a target position
		private Vector2 Seek(Vector2 targetPos)
		{
			Vector2 desiredVelocity = (targetPos - m_entity.Position).normalized * m_entity.MaxSpeed;

  			return (desiredVelocity - m_entity.Velocity);
		}
		
		//this behavior returns a vector that moves the Agent away
		//from a target position
		private Vector2 Flee(Vector2 targetPos)
		{
			//only flee if the target is within 'panic distance'. Work in distance
			//squared space.
			const float panicDistanceSq = 100.0f * 100.0f;

			Vector2 fleeVec = m_entity.Position - targetPos;

			if ( fleeVec.sqrMagnitude > panicDistanceSq )
			{
				return Vector2.zero;
			}

			Vector2 desiredVelocity = fleeVec.normalized * m_entity.MaxSpeed;

			return desiredVelocity - m_entity.Velocity;
		}
		
		//this behavior is similar to seek but it attempts to arrive 
		//at the target position with a zero velocity
		private Vector2 Arrive(Vector2 targetPos, Deceleration deceleration)
		{
			Vector2 toTarget = targetPos - m_entity.Position;
			
			//calculate the distance to the target
			float dist = toTarget.magnitude;
			
			if (dist > 0)
			{
				//because Deceleration is enumerated as an int, this value is required
				//to provide fine tweaking of the deceleration..
				const float decelerationTweaker = 0.3f;
				
				//calculate the speed required to reach the target given the desired
				//deceleration
				float speed =  dist / ((float)deceleration * decelerationTweaker);     
				
				//make sure the velocity does not exceed the max
				speed = Mathf.Min(speed, m_entity.MaxSpeed);
				
				//from here proceed just like Seek except we don't need to normalize 
				//the ToTarget vector because we have already gone to the trouble
				//of calculating its length: dist. 
				Vector2 desiredVelocity =  toTarget * speed / dist;
				
				return desiredVelocity - m_entity.Velocity;
			}
			
			return Vector2.zero;
		}
		
		//this behavior predicts where an Agent will be in time T and seeks
		//towards that point to intercept it.
		private Vector2 Pursuit(IEntity evader)
		{
			//if the evader is ahead and facing the agent then we can just seek
			//for the evader's current position.
			Vector2 toEvader = evader.Position - m_entity.Position;
			
			float relativeHeading = Vector2.Dot(m_entity.Forward, evader.Forward);

			if (Vector2.Dot(toEvader, m_entity.Forward) > 0
			    && relativeHeading < -0.95) //acos(0.95) = 18 degs
			{
				return Seek(evader.Position);
			}
			
			//Not considered ahead so we predict where the evader will be.
			
			//the lookahead time is propotional to the distance between the evader
			//and the pursuer; and is inversely proportional to the sum of the
			//agent's velocities
			float lookAheadTime = toEvader.magnitude / 
				(m_entity.MaxSpeed + evader.Velocity.magnitude);
			
			//now seek to the predicted future position of the evader
			return Seek(evader.Position + evader.Velocity * lookAheadTime);
		}
		
		//this behavior maintains a position, in the direction of offset
		//from the target vehicle
		private Vector2 OffsetPursuit(IEntity leader, Vector2 offset)
		{
			//calculate the offset's position in world space
			Vector2 worldOffsetPos = leader.Position + offset;

			Vector2 toOffset = worldOffsetPos - m_entity.Position;
			
			//the lookahead time is propotional to the distance between the leader
			//and the pursuer; and is inversely proportional to the sum of both
			//agent's velocities
			float lookAheadTime = toOffset.magnitude / 
				(m_entity.MaxSpeed + leader.Velocity.magnitude);
			
			//now Arrive at the predicted future position of the offset
			return Arrive(worldOffsetPos + leader.Velocity * lookAheadTime, Deceleration.fast);
		}
		
		//this behavior attempts to evade a pursuer
		private Vector2 Evade(IEntity pursuer)
		{
			// Not necessary to include the check for facing direction this time
			Vector2 toPursuer = pursuer.Position - m_entity.Position;
			
			//uncomment the following to have Evade only consider pursuers 
			//within a 'threat range'
			/*
			const double threatRange = 100.0;
			if (toPursuer.sqrMagnitude > ThreatRange * ThreatRange)
				return Vector2D();
			*/

			//the lookahead time is propotional to the distance between the pursuer
			//and the pursuer; and is inversely proportional to the sum of the
			//agents' velocities
			float lookAheadTime = toPursuer.magnitude / 
				(m_entity.MaxSpeed + pursuer.Velocity.magnitude);
			
			//now flee away from predicted future position of the pursuer
			return Flee(pursuer.Position + (pursuer.Velocity * lookAheadTime));
		}
		
		//this behavior makes the Agent wander about randomly
		private Vector2 Wander()
		{
			m_wanderTarget += UnityEngine.Random.insideUnitCircle;// * jitterThisTimeSlice;

			//reproject this new vector back on to a unit circle
			m_wanderTarget.Normalize();
			
			//increase the length of the vector to the same as the radius
			//of the wander circle
			m_wanderTarget *= m_wanderRadius;
			
			//move the target into a position WanderDist in front of the agent
			Vector2 target = m_wanderTarget + new Vector2(m_wanderDistance, 0);

			// Convert to world space
			m_entity.Transformation.TransformVector(target);

			//and steer towards it
			return target - m_entity.Position;
		}
		
		//this returns a steering force which will attempt to keep the Agent 
		//away from any obstacles it may encounter
		private Vector2 ObstacleAvoidance()
		{
			Vector2 avoidanceVector = Vector2.zero;
			
			foreach (Vector2 feeler in m_entity.Feelers)
			{
				if (Physics.Raycast(m_entity.Position, feeler.normalized, feeler.magnitude, 1 << 9))
				{
					avoidanceVector += feeler;
				}
			}
			
			avoidanceVector *= -1;
			
			return avoidanceVector;
		}
		
		//this returns a steering force which will keep the Agent away from any
		//walls it may encounter
		private Vector2 WallAvoidance()
		{
			Vector2 avoidanceVector = Vector2.zero;

			foreach (Vector2 feeler in m_entity.Feelers)
			{
				if (Physics.Raycast(m_entity.Position, feeler.normalized, feeler.magnitude, 1 << 8))
				{
					avoidanceVector += feeler;
				}
			}
		
			avoidanceVector *= -1;

			return avoidanceVector;
		}
		
		
		//given a series of Vector2s, this method produces a force that will
		//move the Agent along the waypoints in order
		private Vector2 FollowPath()
		{
			//todo:
			return Vector2.zero;
		}
		
		//this results in a steering force that attempts to steer the vehicle
		//to the center of the vector connecting two moving Actors.
		private Vector2 Interpose(IEntity entityA, IEntity entityB)
		{
			//first we need to figure out where the two agents are going to be at 
			//time T in the future. This is approximated by determining the time
			//taken to reach the mid way point at the current time at at max speed.
			Vector2 midPoint = (entityA.Position + entityB.Position) / 2.0f;
			
			float timeToReachMidPoint = (m_entity.Position - midPoint).magnitude /
				m_entity.MaxSpeed;
			
			//now we have T, we assume that agent A and agent B will continue on a
			//straight trajectory and extrapolate to get their future positions
			Vector2 posA = entityA.Position + entityA.Velocity * timeToReachMidPoint;
			Vector2 posB = entityB.Position + entityB.Velocity * timeToReachMidPoint;
			
			//calculate the mid point of these predicted positions
			midPoint = (posA + posB) / 2.0f;
			
			//then steer to Arrive at it
			return Arrive(midPoint, Deceleration.fast);
		}
		
		//given another Agent position to hide from and a list of BaseGameAgents this
		//method attempts to put an obstacle between itself and its opponent
		private Vector2 Hide(IEntity hunter, List<IEntity> obstacles)
		{
			//todo:
			return Vector2.zero;
		}

		// -- Group Behaviors -- //
		
		private Vector2 Cohesion(List<IEntity> entities)
		{
			//todo:
			return Vector2.zero;
		}
		
		private Vector2 Separation(List<IEntity> entities)
		{
			//todo:
			return Vector2.zero;
		}
		
		private Vector2 Alignment(List<IEntity> entities)
		{
			//todo:
			return Vector2.zero;
		}
		
		//the following three are the same as above but they use cell-space
		//partitioning to find the neighbors
		private Vector2 CohesionPlus(List<IEntity> entities)
		{
			//todo:
			return Vector2.zero;
		}

		private Vector2 SeparationPlus(List<IEntity> entities)
		{
			//todo:
			return Vector2.zero;
		}

		private Vector2 AlignmentPlus(List<IEntity> entities)
		{
			//todo:
			return Vector2.zero;
		}
		
		/* .......................................................

                       END BEHAVIOR DECLARATIONS

      	.......................................................*/
		
		//---------------------- CalculateWeightedSum ----------------------------
		//
		//  this simply sums up all the active behaviors X their weights and 
		//  truncates the result to the max available steering force before 
		//  returning
		//------------------------------------------------------------------------
		private Vector2 CalculateWeightedSum()
		{
			Vector2 m_steeringForce = Vector2.zero;

			if (On(BehaviorType.wall_avoidance))
			{
				m_steeringForce += WallAvoidance() * m_weightWallAvoidance;
			}
			
			if (On(BehaviorType.obstacle_avoidance))
			{
				m_steeringForce += ObstacleAvoidance() * m_weightObstacleAvoidance;
			}
			
			if (On(BehaviorType.evade) && EvadeTarget != null)
			{
				m_steeringForce += Evade(EvadeTarget) * m_weightEvade;
			}

			//these next three can be combined for flocking behavior (wander is
			//also a good behavior to add into this mix)
//			if (!IsSpacePartitioningOn())
//			{
//				if (On(BehaviorType.separation))
//				{
//				}
//				
//				if (On(BehaviorType.allignment))
//				{
//				}
//				
//				if (On(BehaviorType.cohesion))
//				{
//				}
//			}
//			else
//			{
//				if (On(BehaviorType.separation))
//				{
//				}
//				
//				if (On(BehaviorType.allignment))
//				{
//				}
//				
//				if (On(BehaviorType.cohesion))
//				{
//				}
//			}

			if (On(BehaviorType.wander))
			{
				m_steeringForce += Wander() * m_weightWander;
			}
			
			if (On(BehaviorType.seek))
			{
				m_steeringForce += Seek(SeekTarget) * m_weightSeek;
			}
			
			if (On(BehaviorType.flee))
			{
				m_steeringForce += Flee(FleeTarget) * m_weightFlee;
			}
			
			if (On(BehaviorType.arrive))
			{
				m_steeringForce += Arrive(ArriveTarget, Deceleration.normal) * m_weightArrive;
			}
			
			if (On(BehaviorType.pursuit) && PursuitTarget != null)
			{
				m_steeringForce += Pursuit(PursuitTarget) * m_weightPursuit;
			}
			
			if (On(BehaviorType.offset_pursuit))
			{
				m_steeringForce += OffsetPursuit(PursuitTarget, m_offset) * m_weightOffsetPursuit;
			}
			
//			if (On(BehaviorType.interpose))
//			{
//				m_steeringForce += Interpose() * m_weightInterpose;
//			}
//			
//			if (On(BehaviorType.hide))
//			{
//				m_steeringForce += Hide() * m_weightHide;
//			}
			
			if (On(BehaviorType.follow_path))
			{
				m_steeringForce += FollowPath() * m_weightFollowPath;
			}

			//todo:
			m_steeringForce = m_steeringForce.normalized * m_entity.MaxForce;

			return m_steeringForce;
		}

		//---------------------- CalculatePrioritized ----------------------------
		//
		//  this method calls each active steering behavior in order of priority
		//  and acumulates their forces until the max steering force magnitude
		//  is reached, at which time the function returns the steering force 
		//  accumulated to that  point
		//------------------------------------------------------------------------
		private Vector2 CalculatePrioritized()
		{
			m_steeringForce = Vector2.zero;

			Vector2 force = Vector2.zero;
			
			if (On(BehaviorType.wall_avoidance))
			{
				force = WallAvoidance() * m_weightWallAvoidance;
				
				if (!AccumulateForce(ref m_steeringForce, force)) 
					return m_steeringForce;
			}
			
			if (On(BehaviorType.obstacle_avoidance))
			{
				force = ObstacleAvoidance() * m_weightObstacleAvoidance;
				
				if (!AccumulateForce(ref m_steeringForce, force)) 
					return m_steeringForce;
			}
			
			if (On(BehaviorType.evade) && EvadeTarget != null)
			{
				force = Evade(EvadeTarget) * m_weightEvade;
				
				if (!AccumulateForce(ref m_steeringForce, force))
					return m_steeringForce;
			}
			
			
			if (On(BehaviorType.flee))
			{
				force = Flee(FleeTarget) * m_weightFlee;
				
				if (!AccumulateForce(ref m_steeringForce, force))
					return m_steeringForce;
			}

			//these next three can be combined for flocking behavior (wander is
			//also a good behavior to add into this mix)
//			if (!IsSpacePartitioningOn())
//			{
//				if (On(BehaviorType.separation))
//				{
//				}
//				
//				if (On(BehaviorType.allignment))
//				{
//				}
//				
//				if (On(BehaviorType.cohesion))
//				{
//				}
//			}
//			else
//			{
//				if (On(BehaviorType.separation))
//				{
//				}
//				
//				if (On(BehaviorType.allignment))
//				{
//				}
//				
//				if (On(BehaviorType.cohesion))
//				{
//				}
//			}
			
			if (On(BehaviorType.seek))
			{
				force = Seek(SeekTarget) * m_weightSeek;
				
				if (!AccumulateForce(ref m_steeringForce, force)) 
					return m_steeringForce;
			}

			if (On(BehaviorType.arrive))
			{
				force = Arrive(ArriveTarget, Deceleration.normal) * m_weightArrive;
				
				if (!AccumulateForce(ref m_steeringForce, force)) 
					return m_steeringForce;
			}
			
			if (On(BehaviorType.wander))
			{
				force = Wander() * m_weightWander;
				
				if (!AccumulateForce(ref m_steeringForce, force)) 
					return m_steeringForce;
			}
			
			if (On(BehaviorType.pursuit) && PursuitTarget != null)
			{
				force = Pursuit(PursuitTarget) * m_weightPursuit;
				
				if (!AccumulateForce(ref m_steeringForce, force)) 
					return m_steeringForce;
			}
			
			if (On(BehaviorType.offset_pursuit))
			{
				force = OffsetPursuit(PursuitTarget, m_offset);
				
				if (!AccumulateForce(ref m_steeringForce, force)) 
					return m_steeringForce;
			}
			
//			if (On(BehaviorType.interpose))
//			{
//				force = Interpose() * m_weightInterpose;
//				
//				if (!AccumulateForce(ref m_steeringForce, force)) 
//					return m_steeringForce;
//			}
//			
//			if (On(BehaviorType.hide))
//			{
//				force = Hide() * m_weightHide;
//				
//				if (!AccumulateForce(ref m_steeringForce, force))
//					return m_steeringForce;
//			}

			if (On(BehaviorType.follow_path))
			{
				force = FollowPath() * m_weightFollowPath;
				
				if (!AccumulateForce(ref m_steeringForce, force))
					return m_steeringForce;
			}
			
			return m_steeringForce;
		}

		//---------------------- CalculateDithered ----------------------------
		//
		//  this method sums up the active behaviors by assigning a probabilty
		//  of being calculated to each behavior. It then tests the first priority
		//  to see if it should be calcukated this simulation-step. If so, it
		//  calculates the steering force resulting from this behavior. If it is
		//  more than zero it returns the force. If zero, or if the behavior is
		//  skipped it continues onto the next priority, and so on.
		//
		//  NOTE: Not all of the behaviors have been implemented in this method,
		//        just a few, so you get the general idea
		//------------------------------------------------------------------------
		private Vector2 CalculateDithered()
		{
			const float prWallAvoidance = 0.5f;
			const float prObstacleAvoidance = 0.5f;
			//const float prSeparation = 0.5f;
			const float prFlee = 0.5f;
			const float prEvade = 0.5f;
			//const float prAlignment = 0.5f;
			//const float prCohesion = 0.5f;
			const float prWander = 0.5f;
			const float prSeek = 0.5f;
			const float prArrive = 0.5f;

			//reset the steering force
			m_steeringForce = Vector2.zero;
			
			if (On(BehaviorType.wall_avoidance) 
			    && UnityEngine.Random.Range(0.0f, 1.0f) < prWallAvoidance)
			{
				m_steeringForce = WallAvoidance() * m_weightWallAvoidance / prWallAvoidance;
				
				if (m_steeringForce.magnitude != 0.0f)
				{
					m_steeringForce = m_steeringForce.normalized * m_entity.MaxForce; 
					
					return m_steeringForce;
				}
			}
			
			if (On(BehaviorType.obstacle_avoidance) 
			    && UnityEngine.Random.Range(0.0f, 1.0f) < prObstacleAvoidance)
			{
				m_steeringForce += ObstacleAvoidance() * m_weightObstacleAvoidance / prObstacleAvoidance;
				
				if (m_steeringForce.magnitude != 0.0f)
				{
					m_steeringForce = m_steeringForce.normalized * m_entity.MaxForce; 
					
					return m_steeringForce;
				}
			}

//			if (!IsSpacePartitioningOn())
//			{
//				if (On(BehaviorType.separation)
//				    && UnityEngine.Random.Range(0.0f, 1.0f) < prSeparation)
//				{
//				}
//
//				if (On(BehaviorType.allignment)
//					&& UnityEngine.Random.Range(0.0f, 1.0f) < prAlignment)
//				{
//				}
//				
//				if (On(BehaviorType.cohesion)
//					&& UnityEngine.Random.Range(0.0f, 1.0f) < prCohesion)
//				{
//				}
//			}
//			else
//			{
//				if (On(BehaviorType.separation)
//				    && UnityEngine.Random.Range(0.0f, 1.0f) < prSeparation)
//				{
//				}
//				if (On(BehaviorType.allignment)
//				    && UnityEngine.Random.Range(0.0f, 1.0f) < prAlignment)
//				{
//				}
//				
//				if (On(BehaviorType.cohesion)
//					&& UnityEngine.Random.Range(0.0f, 1.0f) < prCohesion)
//				{
//				}
//			}

			if (On(BehaviorType.flee)
				&& UnityEngine.Random.Range(0.0f, 1.0f) < prFlee)
			{
				m_steeringForce += Flee(FleeTarget) * m_weightFlee / prFlee;
				
				if (m_steeringForce.magnitude != 0.0f)
				{
					m_steeringForce = m_steeringForce.normalized * m_entity.MaxForce;
					
					return m_steeringForce;
				}
			}
			
			if (On(BehaviorType.evade)
			    && EvadeTarget != null
				&& UnityEngine.Random.Range(0.0f, 1.0f) < prEvade)
			{
				m_steeringForce += Evade(EvadeTarget) * m_weightEvade / prEvade;
				
				if (m_steeringForce.magnitude != 0.0f)
				{
					m_steeringForce = m_steeringForce.normalized * m_entity.MaxForce; 
					
					return m_steeringForce;
				}
			}

			if (On(BehaviorType.wander)
			    && UnityEngine.Random.Range(0.0f, 1.0f) < prWander)
			{
				m_steeringForce += Wander() * m_weightWander / prWander;
				
				if (m_steeringForce.magnitude != 0.0f)
				{
					m_steeringForce = m_steeringForce.normalized * m_entity.MaxForce; 
					
					return m_steeringForce;
				}
			}
			
			if (On(BehaviorType.seek)
				&& UnityEngine.Random.Range(0.0f, 1.0f) < prSeek)
			{
				m_steeringForce += Seek(SeekTarget) * m_weightSeek / prSeek;
				
				if (m_steeringForce.magnitude != 0.0f)
				{
					m_steeringForce = m_steeringForce.normalized * m_entity.MaxForce; 
					
					return m_steeringForce;
				}
			}
			
			if (On(BehaviorType.arrive)
				&& UnityEngine.Random.Range(0.0f, 1.0f) < prArrive)
			{
				m_steeringForce += Arrive(ArriveTarget, Deceleration.normal) * 
					m_weightArrive / prArrive;
				
				if (m_steeringForce.magnitude != 0.0f)
				{
					m_steeringForce = m_steeringForce.normalized * m_entity.MaxForce; 
					
					return m_steeringForce;
				}
			}
			
			return m_steeringForce;
		}
		
		//helper method for Hide. Returns a position located on the other
		//side of an obstacle to the pursuer
		private Vector2 GetHidingPosition(Vector2 posOb, float radiusOb, Vector2 posHunter)
		{
			//calculate how far away the agent is to be from the chosen obstacle's
			//bounding radius
			const float distanceFromBoundary = 30.0f;
			float distAway = radiusOb + distanceFromBoundary;
			
			//calculate the heading toward the object from the hunter
			Vector2 toOb = (posOb - posHunter).normalized;
			
			//scale it to size and add to the obstacles position to get
			//the hiding spot.
			return (toOb * distAway) + posOb;
		}







	}
}

